package num

import (
	"math/big"
	"reflect"
	"testing"
)

func TestIntegerRNumberOps(t *testing.T) {
	i1 := Integer(5)
	i2 := Integer(3)

	// RADD
	res := i1.RADD(i2)
	if res.Type() != INTEGER_TYPE {
		t.Fatalf("RADD: expected INTEGER_TYPE, got %v", res.Type())
	}
	if int64(res.(Integer)) != 8 {
		t.Fatalf("RADD: expected 8, got %v", int64(res.(Integer)))
	}

	// RSUB
	res = i1.RSUB(i2)
	if int64(res.(Integer)) != 2 {
		t.Fatalf("RSUB: expected 2, got %v", int64(res.(Integer)))
	}

	// RMUL
	res = i1.RMUL(i2)
	if int64(res.(Integer)) != 15 {
		t.Fatalf("RMUL: expected 15, got %v", int64(res.(Integer)))
	}

	// RDIV (integer division)
	i3 := Integer(15)
	i4 := Integer(3)
	res = i3.RDIV(i4)
	if res.Type() != INTEGER_TYPE {
		t.Fatalf("RDIV: expected INTEGER_TYPE, got %v", res.Type())
	}
	if int64(res.(Integer)) != 5 {
		t.Fatalf("RDIV: expected 5, got %v", int64(res.(Integer)))
	}

	// RDIV non-exact (integer division truncates)
	i5 := Integer(7)
	i6 := Integer(3)
	res = i5.RDIV(i6)
	if res.Type() != INTEGER_TYPE {
		t.Fatalf("RDIV non-exact: expected INTEGER_TYPE, got %v", res.Type())
	}
	if int64(res.(Integer)) != 2 {
		t.Fatalf("RDIV non-exact: expected 2 (7/3 truncated), got %v", int64(res.(Integer)))
	}

	// RDIV divide by zero should panic
	defer func() {
		if r := recover(); r == nil {
			t.Fatalf("RDIV by zero did not panic")
		}
	}()
	i7 := Integer(5)
	i8 := Integer(0)
	i7.RDIV(i8)
}

func TestRationalRNumberOps(t *testing.T) {
	r1 := Rational(*big.NewRat(1, 2))
	r2 := Rational(*big.NewRat(1, 3))

	res := r1.RADD(r2)
	if res.Type() != RATIONAL_TYPE {
		t.Fatalf("Rational RADD: expected RATIONAL_TYPE, got %v", res.Type())
	}
	r := res.(Rational)
	br := (*big.Rat)(&r)
	if br.Cmp(big.NewRat(5, 6)) != 0 {
		t.Fatalf("Rational RADD: expected 5/6, got %s", br.String())
	}

	// RDIV
	res = r1.RDIV(r2) // (1/2)/(1/3) = 3/2
	if res.Type() != RATIONAL_TYPE {
		t.Fatalf("Rational RDIV: expected RATIONAL_TYPE, got %v", res.Type())
	}
	r = res.(Rational)
	br = (*big.Rat)(&r)
	if br.Cmp(big.NewRat(3, 2)) != 0 {
		t.Fatalf("Rational RDIV: expected 3/2, got %s", br.String())
	}
}

func TestRealOps(t *testing.T) {
	r1 := Real(1.5)
	r2 := Real(2.5)

	res := r1.RADD(r2)
	if res.Type() != REAL_TYPE {
		t.Fatalf("Real RADD: expected REAL_TYPE, got %v", res.Type())
	}
	r := res.(Real)
	if float64(r) != 4.0 {
		t.Fatalf("Real RADD: expected 4.0, got %v", float64(r))
	}

	// RDIV
	res = r1.RDIV(r2)
	if res.Type() != REAL_TYPE {
		t.Fatalf("Real RDIV: expected REAL_TYPE, got %v", res.Type())
	}
	r = res.(Real)
	if float64(r) != 0.6 {
		t.Fatalf("Real RDIV: expected 0.6, got %v", float64(r))
	}
}

func TestComplexOps(t *testing.T) {
	c1 := Complex{RealPart: Integer(1), ImagPart: Integer(2)}
	c2 := Complex{RealPart: Integer(3), ImagPart: Integer(4)}

	res := c1.ADD(c2)
	if res.Type() != COMPLEX_TYPE {
		t.Fatalf("Complex ADD: expected COMPLEX_TYPE, got %v", res.Type())
	}
	c := res.(Complex)
	if int64(c.RealPart.(Integer)) != 4 || int64(c.ImagPart.(Integer)) != 6 {
		t.Fatalf("Complex ADD: expected (4+6i), got (%v+%vi)", c.RealPart.String(), c.ImagPart.String())
	}

	// MUL: (1+2i)*(3+4i) = (1*3 - 2*4) + (1*4 + 2*3)i = -5 + 10i
	res = c1.MUL(c2)
	if res.Type() != COMPLEX_TYPE {
		t.Fatalf("Complex MUL: expected COMPLEX_TYPE, got %v", res.Type())
	}
	c = res.(Complex)
	if int64(c.RealPart.(Integer)) != -5 || int64(c.ImagPart.(Integer)) != 10 {
		t.Fatalf("Complex MUL: expected (-5+10i), got (%v+%vi)", c.RealPart.String(), c.ImagPart.String())
	}
}

func TestStringRepresentations(t *testing.T) {
	// Integer
	i := Integer(3)
	if i.String() != "3" {
		t.Fatalf("Integer String: expected '3', got '%s'", i.String())
	}

	// Real
	r := Real(1.5)
	if r.String() != "1.5" {
		t.Fatalf("Real String: expected '1.5', got '%s'", r.String())
	}

	// Complex formatting follows current implementation: "(%s)+(%s)i"
	c := Complex{RealPart: Integer(3), ImagPart: Integer(-4)}
	expected := "(3)+(-4)i"
	if c.String() != expected {
		t.Fatalf("Complex String: expected '%s', got '%s'", expected, c.String())
	}
}

func TestRNumberCMPBasics(t *testing.T) {
	// Integer vs Integer
	i1 := Integer(5)
	i2 := Integer(3)
	if RNumber_CMP(i1, i2) != 1 {
		t.Fatalf("RNumber_CMP: expected 1, got %d", RNumber_CMP(i1, i2))
	}

	// Rational vs Rational
	r1 := Rational(*big.NewRat(1, 2))
	r2 := Rational(*big.NewRat(2, 3))
	if RNumber_CMP(r1, r2) != -1 {
		t.Fatalf("RNumber_CMP: expected -1, got %d", RNumber_CMP(r1, r2))
	}

	// Cross-type: Integer vs Real
	i3 := Integer(3)
	r3 := Real(3.0)
	if RNumber_CMP(i3, r3) != 0 {
		t.Fatalf("RNumber_CMP: expected 0 (3 == 3.0), got %d", RNumber_CMP(i3, r3))
	}

	// Use reflect.DeepEqual to ensure rounding/normalization does not hide bugs
	if !reflect.DeepEqual(int64(Integer(5)), int64(5)) {
		t.Fatalf("reflect sanity check failed")
	}

	// Real comparisons
	re1 := Real(1.5)
	re2 := Real(2.5)
	re3 := Real(1.5)

	if RNumber_CMP(re1, re2) != -1 {
		t.Errorf("Expected -1 (1.5 < 2.5), got %d", RNumber_CMP(re1, re2))
	}

	if RNumber_CMP(re2, re1) != 1 {
		t.Errorf("Expected 1 (2.5 > 1.5), got %d", RNumber_CMP(re2, re1))
	}

	if RNumber_CMP(re1, re3) != 0 {
		t.Errorf("Expected 0 (1.5 == 1.5), got %d", RNumber_CMP(re1, re3))
	}

	// Test cross-type comparisons
	i4 := Integer(2)
	r4 := Rational(*big.NewRat(3, 2)) // 1.5

	if RNumber_CMP(i4, r4) != 1 {
		t.Errorf("Expected 1 (2 > 3/2), got %d", RNumber_CMP(i4, r4))
	}

	// Rational to Real - using a more precise comparison
	r5 := Rational(*big.NewRat(1, 3)) // ~0.333333...
	re4 := Real(0.5)

	if RNumber_CMP(r5, re4) != -1 {
		t.Errorf("Expected -1 (1/3 < 0.5), got %d", RNumber_CMP(r5, re4))
	}

	r5 = Rational(*big.NewRat(6, 5)) // 6/5 = 1.2
	re4 = Real(1.2)

	// Given float64 conversion, 6/5 compares equal to 1.2 here.
	if RNumber_CMP(r5, re4) != 0 {
		t.Errorf("Expected 6/5 == 1.2 (after conversion), got %d", RNumber_CMP(r5, re4))
	}

	// Integer to Real
	i5 := Integer(3)
	re5 := Real(3.0)

	if RNumber_CMP(i5, re5) != 0 {
		t.Errorf("Expected 0 (3 == 3.0), got %d", RNumber_CMP(i5, re5))
	}

	// Edge cases
	// Negative numbers
	i6 := Integer(-5)
	i7 := Integer(-3)

	if RNumber_CMP(i6, i7) != -1 {
		t.Errorf("Expected -1 (-5 < -3), got %d", RNumber_CMP(i6, i7))
	}

	// Large numbers
	i8 := Integer(1000000)
	i9 := Integer(999999)

	if RNumber_CMP(i8, i9) != 1 {
		t.Errorf("Expected 1 (1000000 > 999999), got %d", RNumber_CMP(i8, i9))
	}
}
